-- @Author: baidwwy
-- @Date:   2018-04-10 22:48:47
-- @Last Modified by:   baidwwy
-- @Last Modified time: 2018-07-24 04:03:31


local ffi = require("ffi")
local bxor = bit.bxor
ffi.cdef[[
    typedef struct
    {
        int16_t flag;
        int16_t qty;
        int32_t len;
    }ggehead;
]]
local ggebuf = class()
local _flag = 14138 --包头识别
function ggebuf:初始化(len,psd)
    self.buflen    = len or 4096 --缓存大小
    self.bodylen   = self.buflen - ffi.sizeof("ggehead")--包体长度
    self.data      = ffi.new('int8_t[?]',self.buflen)
    self.head      = ffi.cast('ggehead*',self.data)

    self.wint      = ffi.cast('int*',self.data+ffi.sizeof("ggehead"))--写数值
    self.len       = 0
    self.lock      = true
    self.psd       = psd or 'ggelua'
end
function ggebuf:置密码(psd)
    self.psd = psd
end
function ggebuf:打包重置()--0包头，1序号，2长度，3数量
    self.i    = 0
    self.len  = ffi.sizeof("ggehead")
    self.lock = false
end
function ggebuf:添加数值(v)
    if self.len+4 <=self.buflen and not self.lock then
        self.wint[self.i] = v
        self.i   = self.i +1
        self.len = self.len +4
    else
        error(string.format('添加失败:规则错误或者数据过长.(%s)', v))
    end
end
function ggebuf:添加文本(v)
    if self.len+#v <=self.buflen and not self.lock then
        ffi.copy(self.data+self.len,v,#v)
        self.len  = self.len + #v
        self.lock = true
    else
        error(string.format('添加失败:规则错误或者数据过长.(%s)', #v))
    end
end
function ggebuf:打包完成()
    self.head.flag = _flag--头标识
    self.head.qty = self.i
    self.head.len = self.len - ffi.sizeof("ggehead")
    self.lock     = true
end
function ggebuf:添加数据( ... )
    local arg = {...}
    if #arg <5000 then
        self:打包重置()
        for i,v in ipairs(arg) do
            if type(v) == 'number' then
                self:添加数值(v)
            elseif type(v) == 'string' then
                self:添加文本(v)
            else
                error(string.format("添加失败:不支持的数据:%s", type(v)))
            end
        end
        self:打包完成()
        if self.psd then
            local ptr = self.data+ffi.sizeof("ggehead")
            local psd = ffi.cast('int8_t*',self.psd)
            local len = self.head.len
            local k,plen = 0,#self.psd
            for i=0,len-1 do
                ptr[i]=bxor(ptr[i],psd[k]);k=k+1
                if k == plen then k=0 end
            end
        end
        return tonumber(ffi.cast("intptr_t",self.data)),self.len
    else
        error("参数过多.")
    end
end
--=========================解包========================================
function ggebuf:取头长()--0包头，1序号，2长度
    return ffi.sizeof("ggehead")
end
function ggebuf:校验头(info)--成功返回包体长度
    if self.head.flag == _flag and self.head.len >0 and self.head.len <=self.bodylen then
        info.qty = self.head.qty
        --info.len = self.head.len
        return self.head.len
    else
        info.qty = 0
        return 0
    end
end
function ggebuf:取数据(info)
    local num,len = info.qty,info.len
    local r = {}
    local stroff = num*ffi.sizeof("int")
    if num >= 0 and num<5000 and stroff < self.bodylen then
        if self.psd then
            local ptr = self.data
            local psd = ffi.cast('int8_t*',self.psd)
            local k,plen = 0,#self.psd
            for i=0,len-1 do
                ptr[i]=bxor(ptr[i],psd[k]);k=k+1
                if k == plen then k=0 end
            end
        end
        local rint = ffi.cast('int*',self.data)--读数值
        for i=0,num-1 do
            table.insert(r, rint[i])
        end
        if len > stroff then
            table.insert(r, ffi.string(self.data+stroff,len-stroff))
        end
    end
    return r
end
function ggebuf:取指针()
    return tonumber(ffi.cast("intptr_t",self.data))--指针
end
function ggebuf:取长度()
    return self.len
end
return ggebuf